package com.smart.data;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;

import org.yucc.PinyinHelper;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.util.Log;
import android.util.SparseIntArray;

public class DialerAccessor implements ContactAccessor {
	public static final String TAG = "DialerAccessor";
	
	private static final boolean DEBUG = false;
	
	private static DialerAccessor accessor  ;
	private static ContentResolver resolver ;
	
	private Context mContext ;
	
	/**
	 * Contact集合使用的排序起
	 */
	private MyComparator comparator = new MyComparator() ;
	
	/**
	 * 输入第一个字符时根据此HashMap找到一个集合，表示以该字符为入口的TreeNode的集合
	 */
	private HashMap<String, ArrayList<TreeNode>> mCacheMap = new HashMap<String, ArrayList<TreeNode>>() ;
	
	/**
	 * 保存已经匹配出来的联系人ID，防止添加相同联系人
	 */
	private ArrayList<Integer> matchedContactsID = new ArrayList<Integer>() ;
	
	/**
	 * startQuery()方法最后返回的结果集
	 */
	private ArrayList<Contact> results = new ArrayList<Contact>(1) ;
	
	/**
	 * 保存联系人的Contact_id到 对应的contact对象在allList集合中的位置，在最后queryLoop方法得到一个TreeNode的集合的时候，
	 * 可以根据每个TreeNode节点中的id得到对应Contact对象
	 */
	private static SparseIntArray idToIndex = new SparseIntArray();
	/**
	 * 数据查询出来后保存的联系人集合
	 */
	private ArrayList<Contact> allList ;
	
	/**
	 *  需要的联系人信息，可以根据需求添加
	 */
	static final String[] CONTACTS_PROJECTION = new String[] { 
		Phone._ID, // 0
		Phone.CONTACT_ID, // 1
		Phone.NUMBER, // 2
		Phone.DISPLAY_NAME ,// 3
		Phone.TYPE ,  //4
		Phone.PHOTO_ID, //5
		};

	private DialerAccessor(Context context) {
		// TODO Auto-generated constructor stub
		mContext = context ;
		resolver = mContext.getContentResolver() ;
	}
	
	/**
	 * 获取访问实例对象，单例模式
	 * @param context 应用的context上下文
	 * return 智能拨号操作对象
	 */
	public static synchronized DialerAccessor getInstance(Context context){
		if (accessor == null) {
			accessor = new DialerAccessor(context) ;
		}
		return accessor ;
	}

	/**
	 *  初始化Accessor的方法，在此方法调用后，才可以调用startQuery方法执行匹配操作
	 */
	public void init() {
		// TODO Auto-generated method stub
		if(allList != null) allList.clear() ;
		else allList = new ArrayList<Contact>() ;
		long start = System.nanoTime();
		Cursor localCursor = queryFromContentProvider2();
		Log.v(TAG, " query count = " + localCursor.getCount() + "---query time = " + (System.nanoTime() - start) * 1e-9
				+ ".s");
		Contact contact = null;
		final int CONTACT_ID_INDEX = 1;
		final int PHONE_NUMBER_INDEX = 2;
		final int DISPLAY_NAME_INDEX = 3;
		final int PHONE_TYPE_INDEX = 4;
		final int PHOTO_ID_INDEX = 5;
		
		int index = 0 ;
		matchedContactsID.clear();
		idToIndex.clear();
		while (localCursor.moveToNext()) {
			contact = new Contact();
			contact.id = localCursor.getInt(CONTACT_ID_INDEX);
			contact.phoneNumber = localCursor.getString(PHONE_NUMBER_INDEX);
			contact.dstPhoneNumber = replaceString(contact.phoneNumber,"-");
			contact.displayName = localCursor.getString(DISPLAY_NAME_INDEX);
			contact.phoneType = localCursor.getInt(PHONE_TYPE_INDEX);
			contact.photo_id = localCursor.getInt(PHOTO_ID_INDEX);

			buildTree(PinyinHelper.toPinYinDigits(contact.displayName), contact.id) ;
			allList.add(contact) ;
			idToIndex.append(contact.id, index);
			index ++ ;
		}
		localCursor.close(); 
		
		Log.v(TAG,
				" >>> TotalInitTime = "
						+ (System.nanoTime() - start) * 1e-9 + ".s");
	} 
	
	/**
	 *  去除联系人电话号码中包含的特定字符
	 *  @param origin 原始的电话号码
	 *  @param toBeReplace 需要被去除的字符串
	 */
	private String replaceString(String origin,String toBeReplace){
		if(origin==null) return "";
		//去掉无用的-字符 
		StringBuilder sb = new StringBuilder() ;
		int index = -1 ;
		int preIndex = 0 ;
		
		while((index = origin.indexOf(toBeReplace,preIndex)) != -1){
			sb.append(origin.substring(preIndex, index)) ;
			preIndex = index+1 ;
		}
		sb.append(origin.substring(preIndex, origin.length())) ;
		return sb.toString() ;
	}
	
	/**
	 *  建立联系人集合的首个字符的入口信息，当输入第一个字符时，根据建立的mCacheMap来获取对应的ArrayList<TreeNode>集合.\n
	 *  
	 *  取T9码数组的每个元素中","之后的的第一个字母创建一个TreeNode（如果是多音字，则有多个"，"），保存对应的元素的index为TreeNode的position值。\n
	 *  保存T9码数组为TreeNode的value值.\n
	 *  初始化每个节点的preWords="".\n
	 *  初始化每个节点的id为联系人ID.\n
	 *  
	 *  @param value 联系人姓名对应的T9码数组，不包括汉字本身
	 *  @param id 联系人对应的ID
	 */
	private void buildTree(String [] value,int id){
		int len = value.length ;
		int index = -1 ,preIndex = 0 ;
		ArrayList<TreeNode> localList ; 
		
		HashMap<String, ArrayList<TreeNode>> localCacheMap = mCacheMap ;
		String temp ; 
		String str ;
		TreeNode node = new TreeNode() ;
		node.value = value ;
		node.id = id ;
		node.preWords = "" ;
		for(int i=0; i<len; i++){
			temp = value[i] ;
			node.position = i ;
			preIndex = 0 ;
			while((index = temp.indexOf(",",preIndex)) != -1){
				preIndex = index + 1;
				str = String.valueOf(temp.charAt(preIndex));
				if((localList = localCacheMap.get(str)) == null){
					localList = new ArrayList<TreeNode>() ;
					localCacheMap.put(str, localList) ;
				}
				localList.add(node.clone()) ;
			}
		}
	}
	
	/**
	 * 查询联系人数据库，以contact_id排序
	 */
	private Cursor queryFromContentProvider2() {
		// TODO Auto-generated method stub
		String str2 = "1=0) UNION ALL SELECT MIN(_id) AS _id,raw_contact_id, data1,display_name,data2 "
				+ ",photo_id,version  FROM view_data_restricted data WHERE (1 AND mimetype "
				+ "= 'vnd.android.cursor.item/phone_v2' AND (raw_contact_id IN (SELECT raw_contact_id FROM "
				+ "name_lookup WHERE name_type IN (2,3,6,5,7))  "
				+ ")) AND ((in_visible_group=1 ) AND ((lookup NOT LIKE '1110i%' and lookup NOT LIKE "
				+ "'3330i%'))) GROUP BY (data1";
		String str3 = " exists(select 1 from view_contacts_restricted  where view_contacts_restricted._id=contact_id" +
				" AND view_contacts_restricted.has_phone_number=1 AND view_contacts_restricted.in_visible_group=1 )) " +
				"AND exists (select 1 from name_lookup where name_lookup.raw_contact_id=raw_contact_id AND name_type in(2,3,6,5,7)) GROUP BY (data4 ";
		
		String str4 = " exists(select 1 from view_contacts_restricted  where view_contacts_restricted._id=contact_id" +
				" AND view_contacts_restricted.has_phone_number=1 AND view_contacts_restricted.in_visible_group=1 )) " +
				"AND exists (select 1 from name_lookup where name_lookup.raw_contact_id=raw_contact_id AND name_type in(2,3,5,6,7) ";
		
		String str5 = " exists(select 1 from view_contacts_restricted  where view_contacts_restricted._id=contact_id" +
				" AND view_contacts_restricted.in_visible_group=1 ) " ;
		
		//Uri localUri = ContactsContract.Data.CONTENT_URI;
		Uri localUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
		Cursor cursor = null ;
		cursor = resolver.query(localUri,
					CONTACTS_PROJECTION, str5, null, "contact_id");
		return cursor ; 
	}

	/**
	 * 匹配联系人号码，不重复添加联系人
	 * @param filter 匹配字符串
	 * @return 电话号码匹配的结果
	 */
	private ArrayList<Contact> phoneQueryList(String filter){
		long startTime = System.nanoTime() ;
		 
		 ArrayList<Contact> result = new  ArrayList<Contact>() ;
		 ArrayList<Contact> localAll = allList ;
		 int len = localAll.size() ;
		 Contact contact = null ;
		 for (int i=0;i<len; i++) {
			 contact = localAll.get(i) ;
			if(contact.dstPhoneNumber.contains(filter)) {
				if(!matchedContactsID.contains(contact.id)){
					matchedContactsID.add(contact.id) ;
					result.add(contact.clone()) ;
				}
			}
		}
		 if(DEBUG) Log.e(TAG, "phoneQueryList the count = " + result.size() );
		 if(DEBUG) Log.e(TAG,"---phoneQueryList  time1 = " +(System.nanoTime()-startTime)*1e-9 +".s  ") ;
		return result; 
	}
	
	/**
	 * 匹配联系人姓名，不重复添加联系人
	 * 
	 * 每一个输入事件，都会根据EditText中的字符串的首个字符得到一个TreeNode的集合。\n
	 * 然后根据这个集合，做递归。每一次递归都会完成一个字符的匹配。更新TreeNode集合\n
	 * 
	 * 最后根据TreeNode的集合，配合Id跟Index的映射，得到联系人Contact的集合\n
	 * 
	 * @param filter 匹配字符串
	 * @return 姓名匹配的结果
	 */
	private ArrayList<Contact> queryLoop(String filter){
		ArrayList<Contact> result = new ArrayList<Contact>();
		//用于获取需要的联系人信息
		ArrayList<Contact> localAll = allList ;
		ArrayList<TreeNode> nodes = null  ; 
		int size = 0 ;
		
		//根据第一个输入的字符找到Node集合，作为初始的集合
		String temp = filter.substring(0, 1) ;
		nodes = (ArrayList<TreeNode>) mCacheMap.get(temp) ;
		if(nodes != null&& (size = nodes.size())>0){
			nodes = (ArrayList<TreeNode>) nodes.clone();
			for(int i=0; i<size; i++){ 
				nodes.get(i).matchLocs = new ArrayList<Integer>();
				nodes.get(i).matchLocs.add(nodes.get(i).position) ;
				nodes.get(i).preWords = temp;        //指定下次从指定位置开始匹配时可以配合进行的字符串
			}
		}
		
		//开始进行递归，查找结果Node集合
		doLoop(nodes, filter) ;
		
		int contactID =0 ;
		int index = -1 ;
		int len = 0 ;
		Contact contact ;
		if(nodes != null&& (len = nodes.size())>0){
			for(int i=0; i<len; i++) {
				contactID = nodes.get(i).id;
				if(DEBUG)		Log.v(TAG, "values="+Arrays.asList(nodes.get(i).value)+",matchLocs = "+nodes.get(i).matchLocs+".");
				if(!matchedContactsID.contains(contactID)){
					matchedContactsID.add(contactID) ;
					index = idToIndex.get(contactID, -1) ;
					contact = localAll.get(index).clone();
					contact.matchLocs = nodes.get(i).matchLocs;
					if(DEBUG)	Log.v(TAG, "contact.displayname="+contact.displayName+".matchLocs="+contact.matchLocs) ;
					if(index != -1)	result.add(contact) ;
				}else{
				}
			}  
		}
		
		return result ;
	}
	
	/**
	 * 递归匹配输入的字符串的每一个字符，更新作为参数传入的ArrayList<TreeNode> source集合\n
	 * 
	 * 1、取filter的首个字符为 c 。循环遍历上一个字符的匹配结果集合。\n
	 * 2、结果集合中的每一个元素都是一个TreeNode。从TreeNode的j=position位置开始遍历TreeNode的value数组。\n
	 * 3、遍历value数组的过程中使用str=(","+preWord+c)做为 if(value[?].contains(str)) 中使用的参数。来确定是否匹配。
	 *    使用","是为了方便区分在value数组存放的同一个汉字对应的多音字。例如： "行" 对应的 数组元素为 ",xing,hang" ;\n
	 * 4、遍历value数组过程时，使用distance>=2来标记跳跃状态。如果支持跳跃匹配。则不break。否则，break； \n
	 * 5、遍历value数组过程时, 即使已经查找到该TreeNode匹配输入filter的首个字符C。只要distance<2。则继续遍历，\n
	 *    因为字符C可能配合上一个字符作为一个汉字的拼音，也可以做为另一个汉字的首字母。\n
	 * 6、遍历value数组过程时，如果发现字符C可以匹配，则要更新TreeNode的preWords=(preWords+c)；同时更新matchLocs。\n
	 * 7、使用clone方法得到独立的TreeNode对象，并复制原有内容。\n
	 *   
	 * 
	 * @param source 在queryLoop中最后用于生成Conntact集合的数据，在每次递归都进行更新。
	 * @param filter 长度逐渐减少的匹配字符串，如果传入的filter参数长度为1，表示在上一次递归已经完成匹配，直接返回。
	 */
	private static void doLoop(ArrayList<TreeNode> source,String filter){
		if(filter.length() == 1) return  ;     	//只有一个字符，停止递归
		
		filter = filter.substring(1, filter.length()) ;
		int size = 0 ;
		if(source != null && (size = source.size())>0){
			ArrayList<TreeNode> tempNodes = new ArrayList<TreeNode>();
			tempNodes.addAll(source);
			source.clear() ;
			TreeNode node ;
			String [] value ;
			StringBuilder sb = new StringBuilder(",");
			int arrLen = 0 ;
			char c  = filter.charAt(0);
			ArrayList<Integer> temp = null ;
			int distance = 0 ;  //初始距离
			int index = -1 ;
			for(int i=0; i<size; i++){
				node = tempNodes.get(i).clone();       //使用clone生成新对象
				value = node.value;  
				arrLen = value.length ;
				distance = 0 ;
				for(int j=node.position; j<arrLen; j++){
					node.position = j ; 
					sb.setLength(1) ;
					if(value[j].contains(sb.append(node.preWords).append(c))){
						node.preWords = sb.substring(1, sb.length()) ;
						temp = node.matchLocs ;
						//如果发现有包含，则把比j大的都删了
						if((index = temp.indexOf(j)) != -1) {
							int len = temp.size();
							for(int k=index+1;k<len;k++){
								temp.remove(k);
							}
						}else temp.add(j) ;
						source.add(node.clone()) ;
						//匹配到后不进行break，将字符做为开头，准备数据
					}                 
					node.preWords = "" ; //不管查到没查到。都要清空前置匹配字符
					//根据需求。遇到空格，是否要继续查找下一个。此处为继续
					if(!", ".equals(value[j]))if(++distance >=2) break ;
				}
			}
			doLoop(source, filter) ;
		}
	}
	
	/**
	 * 开始匹配任务
	 * @param filter 匹配字符串
	 * @return 字符串匹配的结果集
	 */
	public ArrayList<Contact> startQuery(String filter){
		results.clear() ;
		matchedContactsID.clear();
		long start = System.nanoTime() ;
		if(filter.length() > 0) {
			results.addAll(queryLoop(filter));
			results.addAll(phoneQueryList(filter)) ;
			long start1 = System.nanoTime() ;
			Collections.sort(results, comparator) ;
			if(DEBUG)  Log.v(TAG, "sort time="+(System.nanoTime()-start1)*1e-9+".s");
			if(DEBUG)  Log.v(TAG, "startQuery time="+(System.nanoTime()-start)*1e-9+".s");
		}
		return results;
	}
	
	/**
	 * contact排序类
	 * 使用contact的姓名做排序
	 */
	class MyComparator implements Comparator<Contact>{
		private Comparator<Object> comp = Collator.getInstance(Locale.CHINA) ;
		@Override
		public int compare(Contact lhs, Contact rhs) {
			// TODO Auto-generated method stub
			return comp.compare(lhs.displayName, rhs.displayName) ;
		}
	}

}
